#include <stdio.h>
#include <gmp.h>
#include <pthread.h>

//a0 = 6- 4(raiz2)
//y0 = (raiz2) - 1

/*tipos gmpf_t para a thread*/
typedef struct{
    mpf_t a0;
    mpf_t y0;
    mpf_t pi, piAnterior;

    int iteracoes;
}gmpf_t;

void *inicializaY0(mpf_t *y0){
    mpf_t aux;

    mpf_init(aux);

    mpf_init_set_str(aux, "2", 10);
    mpf_sqrt(*y0, aux);

    mpf_init_set_str(aux, "1", 10);
    mpf_sub(*y0, *y0, aux);

    mpf_clears(aux, NULL);
}

void *inicializaA0(mpf_t *a0){
    mpf_t aux;

    mpf_init(aux);

    mpf_init_set_str(aux, "2", 10);
    mpf_sqrt(*a0, aux);

    mpf_init_set_str(aux, "4", 10);
    mpf_mul(*a0, *a0, aux);

    mpf_init_set_str(aux, "6", 10);
    mpf_sub(*a0, aux, *a0);

    mpf_clears(aux, NULL);
}

void *iteradorY(mpf_t *y){
    mpf_t aux;
    mpf_t aux2;

    mpf_init(aux);
    mpf_init(aux2);
    mpf_init_set(aux2, *y);

    //Numerador
    mpf_pow_ui (*y, *y, 4);

    mpf_init_set_str(aux, "1", 10);
    mpf_sub(*y, aux,*y);

    mpf_sqrt(*y, *y);
    mpf_sqrt(*y, *y);

    mpf_init_set_str(aux, "1", 10);
    mpf_sub(*y, aux,*y);

    //Denominador
    mpf_pow_ui (aux2, aux2, 4);

    mpf_init_set_str(aux, "1", 10);
    mpf_sub(aux2, aux,aux2);

    mpf_sqrt(aux2, aux2);
    mpf_sqrt(aux2, aux2);

    mpf_init_set_str(aux, "1", 10);
    mpf_add(aux2, aux, aux2);

    //Resultado Y(k+1)
    mpf_div(*y, *y, aux2);

    mpf_clears(aux, aux2, NULL);
}

void *iteradorA(gmpf_t *vars){
    mpf_t aux;
    mpf_t aux2;

    mpf_init(aux);
    mpf_init(aux2);

    //Primeiro termo
    mpf_init_set_str(aux2, "1", 10);
    mpf_add(aux2, aux2, vars->y0);
    mpf_pow_ui (aux2, aux2, 4);

    mpf_mul(vars->a0, vars->a0, aux2);

    //Segundo termo
    mpf_init_set_str(aux, "1", 10);
    mpf_add(aux,aux,vars->y0);

    mpf_pow_ui (aux2, vars->y0, 2);
    mpf_add(aux,aux,aux2);

    mpf_mul(aux, vars->y0, aux);

    mpf_init_set_str(aux2, "2", 10);
    mpf_pow_ui (aux2, aux2, (2*(vars->iteracoes))+3);

    mpf_mul(aux, aux, aux2);

    //Resultado
    mpf_sub(vars->a0, vars->a0, aux);

    mpf_clears(aux, aux2, NULL);
}

int main(void){
    pthread_t  thread[6];   /*Array com as threads utilizadas*/

    int flag = 0, precisao = 32000000;
    gmpf_t var;

    mpf_set_default_prec(precisao);

    mpf_init(var.a0);
    mpf_init(var.y0);
    mpf_init(var.pi);
    mpf_init(var.piAnterior);

    pthread_create( &thread[0], NULL, (void *)inicializaA0, (void *)&(var.a0)); /*Inicializa a0*/
    pthread_create( &thread[1], NULL, (void *)inicializaY0, (void *)&(var.y0)); /*Inicializa y0*/
    pthread_join( thread[0], NULL);
    pthread_join( thread[1], NULL);

    for(var.iteracoes = 0;;var.iteracoes++){
        mpf_init_set_str(var.pi, "1", 10);
        mpf_div(var.pi, var.pi, var.a0);

        if(var.iteracoes == 0)
            gmp_printf ("pi%d: %.70Ff\n", var.iteracoes, var.pi);
        else{
            if(!mpf_eq(var.piAnterior, var.pi, 32000000))
                gmp_printf ("pi%d: %.70Ff\n", var.iteracoes, var.pi);
            else
                break;
        }

        pthread_create( &thread[2], NULL, (void *)iteradorY, (void *)&(var.y0)); /*Inicializa a0*/
        pthread_join( thread[2], NULL);
        pthread_create( &thread[3], NULL, (void *)iteradorA, (void *)&(var)); /*Inicializa a0*/
        pthread_join( thread[3], NULL);

        mpf_set(var.piAnterior, var.pi);
    }

    /* free used memory */
    mpf_clears(var.a0, var.y0, var.pi, var.piAnterior, NULL);

    return 1;
}

